#ifndef PRECISION_TIMER_H
#define PRECISION_TIMER_H

#include <windows.h>
#include <cassert>

namespace MyCommon
{


	class PrecisionTimer
	{
	private:
		LONGLONG m_CurrentTime;
		LONGLONG m_LastTime;
		LONGLONG m_LastTimeInTimeElapsed;
		LONGLONG m_NextTime;
		LONGLONG m_StartTime;
		LONGLONG m_FrameTime;
		LONGLONG m_PerfCountFreq;

		double m_TimeElapsed;
		double m_LastTimeElapsed;
		double m_TimeScale;

		double m_NormalFPS;
		double m_SlowFPS;

		bool m_bStarted;

		//if true a call to TimeElapsed() will return 0 if the current
		//time elapsed is much smaller than the previous. Used to counter
		//the problems associated with the user using menus/resizing/moving 
		//a window etc
		bool m_bSmoothUpdates;

	public:
		PrecisionTimer(void);
		PrecisionTimer(double fps);
		
		void Start();

		//
		// determines if enough time has passed to move onto next frame
		inline bool    ReadyForNextFrame();

		inline double TimeElapsed();

		double CurrentTime()
		{
			QueryPerformanceCounter((LARGE_INTEGER*)&m_CurrentTime);
			return (m_CurrentTime-m_StartTime)*m_TimeScale;
		}

		bool Started() const {return m_bStarted;}
		void SmoothUpdatesOn(){m_bSmoothUpdates = true;}
		void SmoothUpdatesOff(){m_bSmoothUpdates = false;}



	};


	//-------------------------ReadyForNextFrame()-------------------------------
	//
	//  returns true if it is time to move on to the next frame step. To be used if
	//  FPS is set.
	//
	//----------------------------------------------------------------------------
	inline bool PrecisionTimer::ReadyForNextFrame()
	{
		assert(m_NormalFPS && "PrecisionTimer::ReadyForNextFrame<No FPS set in timer>");
		QueryPerformanceCounter((LARGE_INTEGER*)&m_CurrentTime);
		if (m_CurrentTime>m_NextTime)
		{
			m_TimeElapsed = (m_CurrentTime-m_LastTime)*m_TimeScale;
			m_LastTime = m_CurrentTime;

			//update time to render next frame
			m_NextTime = m_CurrentTime+m_FrameTime;
			return true;
		}
		return false;
	}

	/** returns time elapsed since last call to this function.
		
	*/
	inline double PrecisionTimer::TimeElapsed()
	{
		m_LastTimeElapsed = m_TimeElapsed;
		QueryPerformanceCounter( (LARGE_INTEGER*) &m_CurrentTime);
		m_TimeElapsed = (m_CurrentTime - m_LastTimeInTimeElapsed) * m_TimeScale;
		m_LastTimeInTimeElapsed    = m_CurrentTime;

		const double Smoothness = 5.0;
		if (m_bSmoothUpdates)
		{
			if (m_TimeElapsed < (m_LastTimeElapsed * Smoothness))
			{
				return m_TimeElapsed;
			}
			else
			{
				return 0.0;
			}
		}
		else
		{
			return m_TimeElapsed;
		}
	}
}

#endif